package com.gwtintegration.smartrf.builder;

import java.util.ArrayList;
import java.util.List;

import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.user.rebind.SourceWriter;
import com.gwtintegration.smartrf.client.Attribute;

public class ProxyConverterBuilder {

	private List<FieldConverterBuilder> fieldConverterBuilders;

	private final JClassType proxyType;

	private final ConverterBuilder converterBuilder;
	private final TreeLogger logger;
	private final GeneratorContext context;

	private String proxyConverterName;

	public ProxyConverterBuilder(ConverterBuilder converterBuilder, JClassType proxyType) throws UnableToCompleteException {

		this.converterBuilder = converterBuilder;

		this.logger = converterBuilder.getLogger();
		this.context = converterBuilder.getContext();

		this.proxyType = proxyType;
		this.proxyConverterName = proxyType.getQualifiedSourceName().replaceAll("\\.", "\\$") + "Converter";
		this.fieldConverterBuilders = new ArrayList<FieldConverterBuilder>();

		for (JMethod method : proxyType.getMethods()) {
			if (!JMethodUtils.isGetter(method)) {
				continue;
			}

			Attribute attribute = JMethodUtils.getAttribute(method);
			if (attribute != null && attribute.ignore()) {
				continue;
			}

			fieldConverterBuilders.add(FieldConverterBuilder.create(method, this));
		}
	}

	public void build(SourceWriter sourceWriter) throws UnableToCompleteException {

		sourceWriter.println("public class %s extends ProxyConverter<%s> {", proxyConverterName, proxyType.getName());
		sourceWriter.indent();

		buildToRecord(sourceWriter);

		sourceWriter.println();

		buildToProxy(sourceWriter);

		sourceWriter.println();

		buildHasSameId(sourceWriter);

		sourceWriter.outdent();
		sourceWriter.println("}");
	}

	private void buildHasSameId(SourceWriter sourceWriter) throws UnableToCompleteException {
		sourceWriter.println("public boolean hasSameId(Record record, %s proxy) {", proxyType.getName());
		sourceWriter.indent();

		sourceWriter.println("if(record == null || proxy == null) {");
		sourceWriter.indentln("return false;");
		sourceWriter.println("}");
		sourceWriter.println();

		for (FieldConverterBuilder fieldConverterBuilder : fieldConverterBuilders) {
			if (fieldConverterBuilder.buildEquals(sourceWriter)) {
				sourceWriter.println();
			}
		}

		sourceWriter.println("return true;");

		sourceWriter.outdent();
		sourceWriter.println("}");
	}

	private void buildToProxy(SourceWriter sourceWriter) throws UnableToCompleteException {
		sourceWriter.println("public %s toProxy(Record record, RequestContext requestContext, ConverterCache converterCache) {", proxyType.getName());
		sourceWriter.indent();

		sourceWriter.println("if(record == null || record.getJsObj() == null) {");
		sourceWriter.indentln("return null;");
		sourceWriter.println("}");
		sourceWriter.println();

		sourceWriter.println("if(converterCache.contains(record)) {");
		sourceWriter.indent();
		sourceWriter.println("return (%s)converterCache.get(record);", proxyType.getName());
		sourceWriter.outdent();
		sourceWriter.println("}");
		sourceWriter.println();

		sourceWriter.println("%s proxy = this.getProxy(record);", proxyType.getName());
		sourceWriter.println("if(proxy == null || !hasSameId(record, proxy)) {");
		sourceWriter.indent();
		sourceWriter.println("proxy = requestContext.create(%s.class);", proxyType.getName());
		sourceWriter.outdent();
		sourceWriter.println("} else { ");
		sourceWriter.indent();
		sourceWriter.println("proxy = requestContext.edit(proxy);");
		sourceWriter.outdent();
		sourceWriter.println("}");
		sourceWriter.println();

		sourceWriter.println("converterCache.add(record, proxy);");
		sourceWriter.println();

		for (FieldConverterBuilder fieldConverterBuilder : fieldConverterBuilders) {
			if (fieldConverterBuilder.buildToProxy(sourceWriter)) {
				sourceWriter.println();
			}
		}

		sourceWriter.println("return proxy;");

		sourceWriter.outdent();
		sourceWriter.println("}");
	}

	private void buildToRecord(SourceWriter sourceWriter) throws UnableToCompleteException {

		sourceWriter.println("public Record toRecord(%s proxy, ConverterCache converterCache) {", proxyType.getName());
		sourceWriter.indent();

		sourceWriter.println("if(proxy == null) {");
		sourceWriter.indentln("return null;");
		sourceWriter.println("}");
		sourceWriter.println();

		sourceWriter.println("if(converterCache.contains(proxy)) {");
		sourceWriter.indentln("return (Record)converterCache.get(proxy);");
		sourceWriter.println("}");
		sourceWriter.println();

		sourceWriter.println("ListGridRecord record = new ListGridRecord();");
		sourceWriter.println();

		sourceWriter.println("converterCache.add(proxy, record);");
		sourceWriter.println();

		for (FieldConverterBuilder fieldConverterBuilder : fieldConverterBuilders) {
			if (fieldConverterBuilder.buildToRecord(sourceWriter)) {
				sourceWriter.println();
			}
		}

		sourceWriter.println("this.setProxy(record, proxy);");

		sourceWriter.println();
		sourceWriter.println("return record;");

		sourceWriter.outdent();
		sourceWriter.println("}");
	}

	public String getProxyConverterName() {
		return proxyConverterName;
	}

	public JClassType getProxyType() {
		return proxyType;
	}

	public ConverterBuilder getConverterBuilder() {
		return converterBuilder;
	}

	public TreeLogger getLogger() {
		return logger;
	}

	public GeneratorContext getContext() {
		return context;
	}
}
